<script setup lang="ts">
import {
  NTable,
  NForm,
  NFormItem,
  NInputNumber,
  NInput,
  NSelect,
  NScrollbar,
  NButton,
  NIcon
} from 'naive-ui';
import {
  CheckmarkDoneCircleOutline as SaveIcon,
  OptionsOutline as EditIcon,
  CloseOutline as RemoveIcon
} from '@vicons/ionicons5'
import { ref, watch } from 'vue';

type DataType = 'int8' | 'uint8' | 'int16' | 'uint16' | 'int32' | 'uint32' | 'int64' | 'uint64' | 'float32' | 'float64'
const ipcRenderer = eval('require("electron").ipcRenderer')
const DataTypeOptions = [
  {
    label: 'int8',
    value: 'int8'
  },
  {
    label: 'uint8',
    value: 'uint8'
  },
  {
    label: 'int16',
    value: 'int16'
  },
  {
    label: 'uint16',
    value: 'uint16'
  },
  {
    label: 'int32',
    value: 'int32'
  },
  {
    label: 'uint32',
    value: 'uint32'
  },
  {
    label: 'int64',
    value: 'int64'
  },
  {
    label: 'uint64',
    value: 'uint64'
  },
  {
    label: 'float32',
    value: 'float32'
  },
  {
    label: 'float64',
    value: 'float64'
  }
]

const props = defineProps<{
  options: {
    name: string,
    type: 'TCP' | 'UDP',
    port: number,
    datas: {
      offset: number,
      name: string,
      type: DataType,
      rindex: number,
      rtype: DataType,
      fn: string
    }[]
  }
}>()
const emit = defineEmits<{
  (event: 'remove'): void
}>()
const bindings = ref({} as unknown as {
  name: string,
  type: 'TCP' | 'UDP',
  port: number,
  datas: {
    offset: number,
    name: string,
    type: DataType,
    rindex: string,
    rtype: DataType,
    fn: string
  }[]
})
const editing = ref(false)
const loading = ref(false)

watch(props, () => {
  editing.value = false
  bindings.value = {
    ...props.options,
    datas: props.options.datas.map(v => {
      return {
        ...v,
        rindex: v.rindex + ''
      }
    })
  }
})

const rules = {
  name: [{
    required: true,
    validator(rule: any, value: string) {
      if (!value) {
        return new Error('必须输入游戏名')
      } else if (value.length < 2) {
        return new Error('游戏名太短')
      } else if (value.length > 32) {
        return new Error('游戏名太长')
      }
      return true
    },
    trigger: ['input', 'blur']
  }],
  type: [{
    required: true,
    trigger: ['input', 'blur']
  }],
  port: [{
    required: true,
    validator(rule: any, value: number) {
      if (!value) {
        return new Error('必须输入端口')
      } else if (!/^\d*$/.test(value + '')) {
        return new Error('端口应该为整数')
      } else if (value < 1025 || value > 65535) {
        return new Error('端口设置范围 [1025, 65535]')
      }
      return true
    },
    trigger: ['input', 'blur']
  }]
}

async function onSave() {
  loading.value = true
  if (await ipcRenderer.invoke('modify', JSON.stringify({
    ...bindings.value,
    datas: bindings.value.datas.map(v => {
      return {
        ...v,
        rindex: parseInt(v.rindex)
      }
    })
  }))) {
    editing.value = false
    loading.value = false
    Object.assign(props.options, bindings.value)
  }
}

function onRemove() {
  emit('remove')
}

function onUp(item: { rindex: string }) {
  if (!/^[0-9]*$/.test(item.rindex)) {
    try {
      item.rindex = eval(item.rindex) + ''
    } catch {
      item.rindex = '0'
    }
  }
}

</script>

<template>
  <div class="game-bindings">
    <n-form ref="formRef" label-placement="left" :model="bindings" :rules="rules">
      <n-form-item path="name" label="游戏名称">
        <n-input :disabled="!editing" v-model:value="bindings.name" :show-button="false" />
      </n-form-item>
      <n-form-item path="type" label="监听类型">
        <n-select :disabled="!editing" style="width: 90px" v-model:value="bindings.type"
          :bindings="[{label: 'UDP', value: 'UDP'}, {label: 'TCP', value: 'TCP'}]"></n-select>
      </n-form-item>
      <n-form-item path="port" label="监听端口">
        <n-input-number :disabled="!editing" v-model:value="bindings.port" :show-button="false" />
      </n-form-item>
      <n-button type="error" @click="onRemove" v-if="!editing">
        <n-icon :size="18" style="margin-right: 5px">
          <remove-icon />
        </n-icon>删除
      </n-button>
      <n-button :loading="loading" type="primary" @click="editing ? onSave() : editing=true">
        <n-icon v-if="!loading" :size="18" style="margin-right: 5px">
          <edit-icon v-if="!editing" />
          <save-icon v-else />
        </n-icon>
        {{editing ? '保存' : '编辑'}}
      </n-button>
    </n-form>
    <n-table :bordered="false" size="small">
      <thead>
        <tr>
          <th class="w40 center">偏移</th>
          <th class="w80 center">类型</th>
          <th class="w120">字段</th>
          <th class="w120 center">读取索引</th>
          <th class="w120 center">读取类型</th>
          <th>计算公式</th>
        </tr>
      </thead>
      <n-scrollbar>
        <tbody>
          <tr v-for="item of bindings.datas">
            <td class="w40 center">{{item.offset}}</td>
            <td class="w80 center">{{item.type}}</td>
            <td class="w120">{{item.name}}</td>
            <td class="w120 center">
              <n-input :disabled="!editing" v-model:value="item.rindex" :allow-input="(v: string) => /^[0-9+\-\*/]*$/.test(v + '')" @keyup.enter="onUp(item)" />
            </td>
            <td class="w120 center">
              <n-select :disabled="!editing" :options="DataTypeOptions" v-model:value="item.rtype" />
            </td>
            <td>
              <n-input :disabled="!editing" v-model:value="item.fn" placeholder="" />
            </td>
          </tr>
        </tbody>
      </n-scrollbar>
    </n-table>
  </div>
</template>
    
<style lang="less" scoped>
.game-bindings {
  width: 960px;

  &:deep(.n-form) {
    display: flex;
    padding: 20px 0 0 12px;

    .n-form-item {
      margin-right: 25px;
      --n-feedback-height: 20px !important;
      --n-label-padding: 0 !important;

      .n-form-item-label__asterisk {
        color: transparent;

        &::before {
          content: ':';
          color: var(--n-label-text-color) !important;
        }
      }
    }

    button:first-of-type {
      margin-left: auto;
      margin-right: 5px;
    }
  }

  :deep(.n-table) {
    --n-border-radius: 0 !important;

    .n-scrollbar {
      width: 960px !important;
      max-height: calc(100vh - 45px - 84px - 34.5px) !important;
      overflow-y: overlay;
    }

    tbody {
      display: block;
      width: 100%;
    }

    thead,
    tbody tr {
      width: 100%;
      display: table;
      table-layout: fixed;
    }

    .center {
      text-align: center;
    }

    .w40 {
      width: 40px;
    }

    .w80 {
      width: 80px;
    }

    .w120 {
      width: 120px;
    }
  }
}
</style>
    